#pragma once
#include <iostream>
#include <string>
#include <filesystem> //c++17文件操作
#include "Mutex.hpp"
#include <fstream>
#include <sstream>
#include <ctime>
#include<memory>
#include <unistd.h>

enum class Loglevel
{
    DEBUG, // 做测试
    INFO,
    WARNING, // 出错但是不影响使用
    ERROR,   // 引起程序退出
    FATAL    // 发生重大错误

};

std::string Level2string(Loglevel level) // 将日志等级转换为字符串
{
    switch (level)
    {
    case Loglevel::DEBUG:
        return "DEBUG";
    case Loglevel::ERROR:
        return "ERROR";
    case Loglevel::FATAL:
        return "FATAL";
    case Loglevel::INFO:
        return "INFO";
    case Loglevel::WARNING:
        return "WARNING";
         default:
        return "Unknown";
    }
}

// 20XX-08-04 12:27:03
std::string GetCurrentTime()
{
    // 1. 获取时间戳
    time_t currtime = time(nullptr);

    // 2. 如何把时间戳转换成为20XX-08-04 12:27:03
    struct tm currtm;
    localtime_r(&currtime, &currtm);

    // 3. 转换成为字符串 -- dubug?
    char timebuffer[64];
    snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d",
             currtm.tm_year + 1900,
             currtm.tm_mon + 1,
             currtm.tm_mday,
             currtm.tm_hour,
             currtm.tm_min,
             currtm.tm_sec);

    return timebuffer;
}

// 1.日志刷新
// 基类方法

class LogStrategy // strategy策略
{
public:
    virtual ~LogStrategy() = default; // 使用default让编译器生成默认的析构函数、
    // virtual表示这个析构函数是一个 ​​虚函数​​
    // 当你有一个​​基类指针指向派生类对象​​，并且通过这个​​基类指针 delete 对象​​时：
    // 如果基类的析构函数​​不是虚函数​​，那么只会调用​​基类的析构函数​​，​​派生类的析构函数不会被调用​​，
    // 可能导致派生类中分配的资源没有被释放（内存泄漏等问题）。
    // 如果基类的析构函数是​​虚函数​​，则会先调用​​派生类的析构函数​​，再调用​​基类的析构函数​​，
    // 确保所有资源都被正确释放。
    virtual void SyncLog(const std::string &logmessage) = 0; // 刷新接口
    //=0表示这是一个纯虚函数，
    // 该函数 ​​没有提供默认实现​​（你不能在当前类中为它写函数体）；
    // 拥有这个纯虚函数的类，称为 ​​抽象类（abstract class）​​；
    // ​​任何继承这个类的派生类，必须实现（override）这个纯虚函数，否则派生类也将成为一个抽象类，不能直接实例化对象。​
};

// 显示器刷新策略
class ConsoleLogStrategy : public LogStrategy // 继承基类方法
{
public:
    ~ConsoleLogStrategy() {}
    void SyncLog(const std::string &logmessage) override
    {
        LockGuard lockguard(&_lock);
        std::cout << logmessage << std::endl;
    }

private:
    Mutex _lock;
};

const std::string logdefaultdir = "log";
const static std::string logfilename = "test.log";
// 文件刷新策略
class FileLogStrategy : public LogStrategy
{
public:
    FileLogStrategy(const std::string &dir = logdefaultdir, const std::string filemane = logfilename)
        : _dir_path_name(dir), _filename(filemane)
    {
        LockGuard lockguard(&_lock);
        if (std::filesystem::exists(dir)) // 判断指定目录是否存在
            return;
        try // 异常处理（try-catch）​​ 的基本结构，用于捕获并处理程序运行时可能发生的错误。
        {
            /* code */ // 可能会抛出异常的代码
            std::filesystem::create_directories(_dir_path_name);
            // 用于 ​​递归地创建目录（即创建多级目录）​​返回true/false
        }
        catch (const std::exception &e)
        {
            std::cerr << e.what() << '\n';
        }
    }
    //
    void SyncLog(const std::string &logmessage) override
    {
        {
            LockGuard lockguard(&_lock);
            std::string target = _dir_path_name;
            target += "/";
            target += _filename;

            // 定义一个输出文件流对象
            std::ofstream out(target.c_str(), std::ios::app); // append
            if (!out.is_open())
            {
                return;
            }
            out << logmessage << "\n"; // 可以用out.write写入；俩等价
            out.close();
        }
    }

    ~FileLogStrategy() {}

private:
    std::string _dir_path_name; // 路径  可做拼接
    std::string _filename;

    Mutex _lock; // 加锁
};

//网络刷新
//////////////////

// 1. 定制刷新策略
// 2. 构建完整的日志
class Logger
{
public:
 Logger()
    {
    }
    void EnableConsoleLogStrategy()//激活显示器策略
    {
        _strategy = std::make_unique<ConsoleLogStrategy>();
    }
    void EnableFileLogStrategy()//激活文件策略
    {
        _strategy = std::make_unique<FileLogStrategy>();
    }

    //形成一条完整日志的方式：类部类的方式
    class LogMessage//代表一条完整的日志
    {
    public:
        LogMessage(Loglevel level,std::string& filename,int line,Logger& logger):
        _curr_time(GetCurrentTime()),
        _level(level),
        _pid(getpid()),
        _filename(filename),
        _line(line),
        _logger(logger)
        {
            std::stringstream ss;
                 //它用于在内存中进行字符串的​​流式输入和输出操作​​，
            //结合了 std::istringstream（用于输入）和 std::ostringstream（用于输出）的功能，因此既可以​​从字符串中读取数据​​，也可以​​向字符串中写入数据​​。
            ss << "[" << _curr_time << "] "
               << "[" << Level2string(_level) << "] "
               << "[" << _pid << "] "
               << "[" << _filename << "] "
               << "[" << _line << "]"
               <<  " - ";
               //日志左半部分，右半部分可变较难拼
            _loginfo = ss.str();
            
        }
            template<typename T>
            LogMessage& operator << (const T &info)
            {
            std::stringstream ss;
            ss << info;//将info写入流
            _loginfo += ss.str();
            return *this;
        }
       
        ~LogMessage()
        {
            if(_logger._strategy)
            {
                _logger._strategy->SyncLog(_loginfo);
            }
        }
    private:
        std::string _curr_time;//日志时间
        Loglevel _level;//日志等级
        pid_t _pid; //进程pid
        std::string _filename;
        int _line;  //行号

        //消息内容，（可变的）要合并到loginfo
        std::string _loginfo; //一条合并完成的，完整的日志信息；

        //加一个对外部类的引用
        Logger &_logger;//提供刷新策略

    };
       LogMessage operator()(Loglevel level, std::string filename, int line)
    {
        return LogMessage(level, filename, line, *this);
    } 
    ~Logger()
    {
    }

private:
    std::unique_ptr<LogStrategy> _strategy;
};

Logger logger;

#define LOG(level) logger(level, __FILE__, __LINE__)
#define EnableConsoleLogStrategy() logger.EnableConsoleLogStrategy()
#define EnableFileLogStrategy() logger.EnableFileLogStrategy()